﻿#define test1
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TempSerialPort
{
    public class OMRtemp
    {
        SerialPort _sp;
        private string recivedData;
        public string ReciveData
        {
            get => recivedData;
        }

        public void InitSerialPort(SerialPort sp, string portName = "COM3", int baudRate = 9600, string parity = "Even", int dataBits = 8, string stopBits = "One")
        {
            if (_sp == null) _sp = new SerialPort();
            try
            {
                _sp.PortName = portName;
                _sp.BaudRate = baudRate;
                _sp.Parity = (Parity)Enum.Parse(typeof(Parity), parity);
                _sp.DataBits = dataBits;
                _sp.StopBits = (StopBits)Enum.Parse(typeof(StopBits), stopBits);
                _sp.DataReceived += _sp_DataReceived;
                _sp.Open();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw;
            }

        }

        public void Close()
        {
            if (_sp != null && _sp.IsOpen)
            {
                _sp.DataReceived -= _sp_DataReceived;
                _sp.Close();

            }
        }

        private void _sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
#if test1//避免一次接收中断
               var tempBytes = new List<byte>();
                while (_sp.BytesToRead > 0)
                {

                    byte[] buffer = new byte[_sp.BytesToRead];
                    _sp.Read(buffer, 0, buffer.Length);
                    foreach (byte b in buffer)
                    {
                        tempBytes.Add(b);
                    }
                   System.Threading.Thread.Sleep(20);//超时时间
                }
                      byte[] dataBuf = tempBytes.ToArray();
#elif test//一次接收
                Byte[] dataBuf = new Byte[this._sp.BytesToRead];   //创建接收字节数组
                this._sp.Read(dataBuf, 0, dataBuf.Length);           //读取数据
#endif
            recivedData = ByteToHex(dataBuf);
        }

        /// <summary>
        /// 字节转化16进制
        /// </summary>
        /// <param name="Bytes"></param>
        /// <returns></returns>
        private string ByteToHex(byte[] Bytes)
        {
            string str = string.Empty;
            Bytes.ToList().ForEach(f => str += f.ToString("X2"));
            return str;
        }
        /// <summary>
        /// 带CRC16的窜口发送指令，
        /// </summary>
        /// <param name="strSendMessage">发送的初始内容</param>
        /// <param name="sendFinalStr">带校验后的发送的内容</param>
        public void SendMessage(string strSendMessage, out string sendFinalStr)
        {
            sendFinalStr = "";
            if (_sp == null || !_sp.IsOpen) return;
            var stringArray = strSendMessage.Trim().Split(' ');
            var result = new byte[stringArray.Length];
            for (int i = 0; i < stringArray.Length; i++)
            {
                result[i] = Convert.ToByte(stringArray[i], 16);
            }
            sendFinalStr = SendMessage(_sp, true, ref result);
        }
        /// <summary>
        /// 字节数组发送信息
        /// </summary>
        /// <param name="spPort"></param>
        /// <param name="crc"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private string SendMessage(SerialPort spPort, bool crc, ref byte[] data)
        {
            if (crc)
            {
                data = CRC16(ref data, true);
            }
            spPort.Write(data, 0, data.Length);
            StringBuilder builder = new StringBuilder();
            data.ToList().ForEach(f => builder.Append(f.ToString("X2") + " "));
            return builder.ToString();
        }
        /// <summary>
        /// crc16校验，一般需要高低位反转
        /// </summary>
        /// <param name="data"></param>
        /// <param name="isReverse"></param>
        /// <returns></returns>
        public byte[] CRC16(ref byte[] data, bool isReverse)
        {
            int len = data.Length;
            if (len > 0)
            {
                ushort crc = 0xFFFF;

                for (int i = 0; i < len; i++)
                {
                    crc = (ushort)(crc ^ (data[i]));
                    for (int j = 0; j < 8; j++)
                    {
                        crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                    }
                }
                byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
                byte lo = (byte)(crc & 0x00FF);         //低位置

                return data.Concat(isReverse ? new byte[] { lo, hi } : new byte[] { hi, lo }).ToArray();
            }
            return data;
        }
        /// <summary>
        /// 读取温度,
        /// </summary>
        /// <param name="strSend">最终发送出去的命令，用于检查，可忽略</param>
        /// <param name="strSN">站号，默认01</param>
        /// <param name="strFunN">功能码，默认03</param>
        /// <param name="strAdr">地址，默认20 00</param>
        /// <param name="length">读取长度，默认 00 01</param>
        /// <returns></returns>
        public string ReadTemp(out string strSend,string strSN = "01", string strFunN = "03", string strAdr = "20 00", string length = "00 01")
        {
            string str1 = strSN + " " + strFunN + " " + strAdr + " " + length;
            SendMessage(str1,out strSend);
            System.Threading.Thread.Sleep(100);
            try
            {
                int temp = Convert.ToInt32(recivedData.Substring(6, 4), 16);
                return (temp / 10.0).ToString("f1");
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
        /// <summary>
        /// 设定命令
        /// </summary>
        /// <param name="d">设定温度值，小数</param>
        /// <param name="strSend">最终发送出去的命令，用于检查，可忽略</param>
        /// <param name="strSN">站号，默认01</param>
        /// <param name="strFunN">功能码，默认06</param>
        /// <param name="strAdr">地址，默认27 2D</param>
        public void SetTemp(double d, out string strSend, string strSN = "01", string strFunN = "06",
            string strAdr = "21 03")
        {
            string strvalue = Convert.ToInt32(d * 10).ToString("x2");
            strvalue = "0000".Substring(0, 4 - strvalue.Length) + strvalue;
            strvalue = strvalue.Substring(0, 2) + " " + strvalue.Substring(2, 2);
            string str1 = strSN + " " + strFunN + " " + strAdr + " " + strvalue;
            SendMessage(str1, out strSend);
            System.Threading.Thread.Sleep(100);
        }
    }
}
